package org.easydarwin.blogdemos;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.Surface;


import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * * 文件描述: 视频异步硬解码类
 * 作者:     wsy
 * 版权所属: SPON
 * 创建时间: 2020-07-17
 */
public class SyncVDecode {
    private final String TAG = "rtspSyncVDecode";
    private String MIME_TYPE = "video/avc";
    private MediaCodec mDecoder;
    /**
     * @howard 2019-11-22
     * 缓存需加大，实测需要30多帧，否则会丢失I帧
     */
    private int pcmqueuesize = 15;//10;//howard changed
    private long timeoutCount = 10000;
    private ArrayBlockingQueue<byte[]> H264Queue = new ArrayBlockingQueue<byte[]>(pcmqueuesize);
    private Surface mSurface;
    private int mWidth;
    private int mHeight;
    private VideoDecodeSizeCallback sizeCallback;//解码器视频流宽高回调
    private MediaFormat format;
    private boolean startDecode = false;
    private boolean isObtainWH = false;//是否获取到了视频流的宽和高
    private boolean isFirstKeyFrame = false;//是否第一帧喂的是I帧

    /**
     * 初始化，主要参数为Surface和页面大小
     *
     * @param surface
     * @param Width
     * @param Height
     * @throws IOException
     */
    public void init(Surface surface, int Width, int Height) {
        this.mSurface = surface;
        this.mWidth = Width;
        this.mHeight = Height;
        isObtainWH = false;
        isFirstKeyFrame = false;
        start();
    }

    /**
     * 视频真实大小回调
     *
     * @param sizeCallback
     */
    public void setVideoSizeCallback(VideoDecodeSizeCallback sizeCallback) {
        this.sizeCallback = sizeCallback;
    }

    private void start() {
        try {
            //解码类型
            mDecoder = MediaCodec.createDecoderByType(MIME_TYPE);
        } catch (Exception e) {
            e.printStackTrace();
        }
        mDecoder.setCallback(callback);
    }

    /**
     * 解码器解码数据
     *
     * @param buffer
     * @param length
     */
    public synchronized void putData(byte[] buffer, int length) {
        putData(buffer, length, 264);
    }

    public synchronized void putData(byte[] buffer, int length, int codecName) {
        if (!startDecode) {
            return;
        }
        if (!isObtainWH) {
            Log.d(TAG,"SyncVDecode putData   isObtainWH: " + isObtainWH);
            int[] size = H264SPSPaser.getSizeFromSps(buffer);
            if (size != null && size[0] > 0 && size[1] > 0) {
                mWidth = size[0];
                mHeight = size[1];
                isObtainWH = true;
                isFirstKeyFrame = true;
                if (codecName == 265) {
                    MIME_TYPE = "video/hevc";
                }
                startConfig();
            } else {
                return;
            }
        }
        if (!isFirstKeyFrame) {
            return;
        }
        if (H264Queue.size() >= pcmqueuesize) {
            Log.d(TAG,"H264Queue.size() >= " + pcmqueuesize);
            H264Queue.clear();
        }
        H264Queue.add(buffer);
    }

    MediaCodec.Callback callback = new MediaCodec.Callback() {
        @Override
        public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
            int length = 0;
            try {
                if (H264Queue.isEmpty()) {
                    codec.queueInputBuffer(index, 0, 0, timeoutCount, 0);
                    timeoutCount = timeoutCount + 100;
                    return;
                }
                byte[] frameBuffer = H264Queue.poll();
                if (frameBuffer == null || frameBuffer.length <= 4) {
                    codec.queueInputBuffer(index, 0, 0, timeoutCount, 0);
                    timeoutCount = timeoutCount + 100;
                    return;
                }
                ByteBuffer inputBuffer = codec.getInputBuffer(index);
                inputBuffer.clear();
                /*H264帧格式以00 00 00 01作为起始码,01后面的一个字节用来判断是否含SPS/PPS*/
//                if (frameBuffer[0] == 0 && frameBuffer[1] == 0 && frameBuffer[2] == 0 && frameBuffer[3] == 1) {
                inputBuffer.put(frameBuffer, 0, frameBuffer.length);
                length = frameBuffer.length;
//                } else {
//                    inputBuffer.put(frameBuffer, 0, frameBuffer.length);
//                    length = frameBuffer.length;
//                }
                codec.queueInputBuffer(index, 0, length, timeoutCount, 0);
                timeoutCount = timeoutCount + 100;
                frameBuffer = null;
            } catch (IllegalStateException e) {
                e.printStackTrace();
                resetDecoder();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
            try {
                // 获取输出缓冲(其中包含编解码后数据)
//                ByteBuffer outputBuffer = mDecoder.getOutputBuffer(index);
//                MediaFormat bufferFormat = mDecoder.getOutputFormat(index);
//            LogUtils.d(TAG, "ouputbufferIndex：" + index);
                //为false不会输出到setOnImageAvailableListener里面
                codec.releaseOutputBuffer(index, startDecode);
            } catch (Exception e) {
                e.printStackTrace();
                resetDecoder();
            }
        }

        @Override
        public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {
            Log.d(TAG,"onError：" + e.getDiagnosticInfo());
//            resetDecoder();
        }

        @Override
        public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format2) {
            mWidth = format2.getInteger(MediaFormat.KEY_WIDTH);
            mHeight = format2.getInteger(MediaFormat.KEY_HEIGHT);
            Log.d(TAG,"onOutputFormatChanged：" + mWidth + ":" + mHeight);
            if (sizeCallback != null) {
                sizeCallback.videoSizeCallback(mWidth, mHeight);
            }
        }
    };

    private void resetDecoder() {
        Log.d(TAG,"resetDecoder：");
        try {
            mDecoder.reset();
            mDecoder.setCallback(null);
            mDecoder = MediaCodec.createDecoderByType(MIME_TYPE);
            format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
            mDecoder.setCallback(callback);
            mDecoder.configure(format, mSurface, null, 0);
            mDecoder.start();
        } catch (Exception e1) {
            Log.d(TAG,"resetDecoder：" + e1.getMessage());
            e1.printStackTrace();
        }
    }

    /**
     * 描述功能：停止编码
     */
    public void stopThread() {
        H264Queue.clear();
        startDecode = false;
        try {
            if (mDecoder != null) {
                mDecoder.reset();
                //停止解码，此时可以再次调用configure()方法 ,因此调用后，你可以调用configure()更换分辨率重新start()开始解码。
                //更改分辨率前必须调用stop！！！
                mDecoder.stop();
                mDecoder.release();
            }
            H264Queue.clear();
        } catch (Exception e) {
            e.printStackTrace();
        }
        mDecoder = null;
        mSurface = null;
    }

    /**
     * 编码线程
     */
    public void startDecodeThread() {
        startDecode = true;
    }

    public void startConfig() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
                    mDecoder.configure(format, mSurface, null, 0);
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.d(TAG,"startConfig：" + e.getMessage());
                }
                try {
                    mDecoder.start();
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.d(TAG,"startConfig Decoder.start：" + e.getMessage());
//                    resetDecoder();
                }
            }
        }).start();
    }
}